/*******************************************************************************************
 * Copyright (c) 2006-2009 Alex Robson, Rob Simmons, Craig Israel, and Rachel Twyford
 *
 * Use of this software in any form requires that you have both 
 * read and agreed to the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the 
 * documentation and/or other materials provided with the distribution.
 * Neither the name of Nvigorate nor the names of its contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************************/

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using Nvigorate.Aspect;
using System.Linq;
using Nvigorate.Extensions;
using DynamicMethodFactory=Ninject.Core.Infrastructure.DynamicMethodFactory;

namespace Nvigorate.Common.Reflection
{
    /// <summary>
    /// The Reflector class provides key functionality by simplifying commonly used
    /// reflection applications. This class is used extensively as it allows for dynamic,
    /// run-time defined manipulation of objects.
    /// This current version is designed to work with the 3.5 version of the .Net
    /// framework and has full support for generics.
    /// </summary>
    public sealed partial class Reflector
    {
        #region Internal Members

        internal static BindingFlags privFlag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
                                               BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy;

        internal static BindingFlags pubFlag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase |
                                              BindingFlags.FlattenHierarchy;

        internal static BindingFlags staticFlag = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic |
                                                 BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase;

        internal static BindingFlags useFlag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
                                              BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy;

        internal static bool recursive = true;
        internal static Type dictionaryType = typeof(IDictionary);
        internal static Type listType = typeof(IList);
        internal static Type cloneType = typeof(ICloneable);
        internal static TypeCaster caster = new TypeCaster();
        internal static IAccessor cache = new DynamicAccessorCache();

        internal static Dictionary<Tuple<Type, Type>, bool> _baseTypeCache = new Dictionary<Tuple<Type, Type>, bool>();
        #endregion

        #region Properties

        /// <summary>
        /// Seting this flag to true to indicates that the Reflector will ignore internal, 
        /// internal or protected class elements. A value of false indicates the Reflector
        /// will include internal, internal class elements.
        /// </summary>
        public static bool PublicOnly
        {
            get { return useFlag == pubFlag; }
            set { useFlag = (value) ? pubFlag : privFlag; }
        }

        /// <summary>
        /// Setting this flag true indicates that the Refelctor should include class elements
        /// contained in it's hierarchy (this is done recursively). Setting this flag false
        /// forces the Reflector to look only at class elements defined in the immediate type.
        /// </summary>
        public static bool SearchHierarchy
        {
            get { return recursive; }
            set { recursive = value; }
        }

        #endregion

        #region Static Methods

        /// <summary>
        /// Adds the given number to the member of the instance
        /// </summary>
        /// <param name="target">The instance to alter</param>
        /// <param name="memberName">The member name to add the number to</param>
        /// <param name="value">The amount to add</param>
        public static void AddTo(object target, string memberName, Number value)
        {
            Number original = (Number) Read(target, memberName);
            Write(target, memberName, original + value);
        }

        /// <summary>
        /// Appends the delimiter and field value to the member of the given instance
        /// </summary>
        /// <param name="target">The instance to alter</param>
        /// <param name="memberName">The member name to add the number to</param>
        /// <param name="value">The value to append</param>
        /// <param name="delimiter">The delimiter to use</param>
        public static void AppendTo(object target, string memberName, string value, string delimiter)
        {
            DelimitedBuilder builder = new DelimitedBuilder(delimiter);
            builder.Append(Read(target, memberName).ToString());
            builder.Append(value, false);
            Write(target, memberName, builder.ToString());
        }

        /// <summary>
        /// Dynamically invokes a function on the target object.
        /// </summary>
        /// <param name="target">The target instance to invoke the method on</param>
        /// <param name="methodName">The name of the method to invoke</param>
        /// <param name="arguments">An object array containing th arguments to pass. Note:
        /// arguments must be in the array in the order they are required in the function.</param>
        /// <returns>The result of the invoked method</returns>
        /// <exception cref="ArgumentException">ArgumentException when target is null or
        /// methodName is null or emtpy.</exception>
        /// <exception cref="ReflectorException">If the method is not found, an error is thrown.</exception>
        public static object CallMethod(object target, string methodName, params object[] arguments)
        {
            return CallMethod(target, target.GetType(), methodName, arguments, null);
        }

        /// <summary>
        /// Dynamically invokes a static function on the target type.
        /// </summary>
        /// <param name="type">The target type to invoke the method on</param>
        /// <param name="methodName">The name of the static method to invoke</param>
        /// <param name="arguments">An object array containing th arguments to pass. Note:
        /// arguments must be in the array in the order they are required in the function.</param>
        /// <returns>The result of the invoked method</returns>
        /// <exception cref="ArgumentException">ArgumentException when type is null or
        /// methodName is null or emtpy.</exception>
        /// <exception cref="ReflectorException">If the method is not found, an error is thrown.</exception>
        public static object CallMethod(Type type, string methodName, params object[] arguments)
        {
            return CallMethod(null, type, methodName, arguments, null);
        }

        public static ConstructorInfo GetCopyConstructor(Type type)
        {
            return type.GetConstructors().FirstOrDefault(c =>
                                                             {
                                                                 ParameterInfo[] parameters = c.GetParameters();
                                                                 if(parameters.FirstOrDefault(p => p.ParameterType == type) != null && parameters.Length == 1)
                                                                     return true;
                                                                 return false;
                                                             });
        }

        public static object Construct(Type type, IEnumerable<object> arguments)
        {
            ConstructorInfo constructor = GetConstructor(arguments, type);
            return constructor.Invoke(arguments.ToArray());
        }

        /// <summary>
        /// Return a list field names for the class type
        /// </summary>
        /// <returns>List of field names</returns>
        public static List<string> GetFields(Type type)
        {
            List<string> fieldNames = new List<string>();

            List<FieldInfo> info = new List<FieldInfo>();
            info.AddRange(type.GetFields(useFlag));
            info.AddRange(type.GetFields(staticFlag));

            fieldNames.AddRange(from i in info where i.Name != null && !fieldNames.Contains(i.Name) && !i.Name.StartsWith("~") select i.Name);

            return fieldNames;
        }

        /// <summary>
        /// Return a list field names for the class type
        /// </summary>
        /// <returns>List of field names</returns>
        public static List<FieldInfo> GetFieldInfoList(Type type)
        {
            List<FieldInfo> info = new List<FieldInfo>();
            info.AddRange(type.GetFields(useFlag));
            info.AddRange(type.GetFields(staticFlag));
            
            if(SearchHierarchy && type.BaseType != typeof(object))
                info.AddRange(GetFieldInfoList(type.BaseType));

            return info.Where(f => !f.Name.StartsWith("~")).ToList();
        }

        public static IEnumerable<Type> GetInheritenceChain(Type type)
        {
            var baseType = type.BaseType;
            if(baseType == null || baseType == typeof(object))
                return null;

            var types = new[] { baseType };
            var enumerable = GetInheritenceChain(baseType);
            return enumerable == null ? types : types.Concat(enumerable);
        }

        /// <summary>
        /// Obtains a list of both field and property names for type.
        /// </summary>
        /// <returns>An array of property and member names</returns>
        public static List<string> GetMembers(Type type)
        {
            List<string> list = new List<string>();

            list.AddRange(GetFields(type));
            list.AddRange(GetProperties(type));

            return list;
        }

        /// <summary>
        /// Gets the System.Type of the field or property specified for the class type
        /// </summary>
        /// <param name="type">The target class type</param>
        /// <param name="memberName">The name of the field or property within the target type</param>
        /// <returns>The Type of the field or property</returns>
        /// <exception cref="ReflectorException">Occurs when the member does not exist within the type</exception>
        public static Type GetMemberType(Type type, string memberName)
        {
            return cache.GetMemberType(type, memberName);
        }


        public static MethodCallExpression GetPropertyGetExpression(Type type, string propertyName, Expression instance)
        {
            PropertyInfo propertyInfo = GetPropertyInfo(type, propertyName, true);
            if (propertyInfo == null)
                throw new ReflectorException("Type {0} does not contain a definition for property {1}",
                                             type.FullName, propertyName);

            return Expression.Call(instance, propertyInfo.GetGetMethod());
        }


        public static MemberAssignment GetPropertyBindExpression(Type type, string propertyName, Expression expression)
        {
            PropertyInfo propertyInfo = GetPropertyInfo(type, propertyName, true);
            if (propertyInfo == null)
                throw new ReflectorException("Type {0} does not contain a definition for property {1}",
                                             type.FullName, propertyName);

            return Expression.Bind(propertyInfo, expression);
        }


        public static MethodCallExpression GetPropertySetExpression(Type type, string propertyName, Expression instance, Expression value)
        {
            PropertyInfo propertyInfo = GetPropertyInfo(type, propertyName, true);
            if (propertyInfo == null)
                throw new ReflectorException("Type {0} does not contain a definition for property {1}",
                                             type.FullName, propertyName);

            return Expression.Call(instance, propertyInfo.GetSetMethod(), value);
        }

        /// <summary>
        /// Return a list of property names for the class type
        /// </summary>
        /// <param name="type">The class type to retrieve properties from</param>
        /// <returns>List of property names</returns>
        public static List<string> GetProperties(Type type)
        {
            List<string> list = new List<string>();

            List<PropertyInfo> properties = new List<PropertyInfo>(type.GetProperties(useFlag));
            list.AddRange(from p in properties where p.Name != null select p.Name);

            properties = new List<PropertyInfo>(type.GetProperties(staticFlag));
            list.AddRange(from p in properties where p.Name != null select p.Name);

            return new List<string>(list.Distinct());
        }

        /// <summary>
        /// Return a list of <see cref="PropertyInfo"/> objects that belong to the given class type.
        /// </summary>
        /// <param name="type">The class type to retrieve properties from</param>
        /// <returns>List of <see cref="PropertyInfo"/> in the given type</returns>
        public static List<PropertyInfo> GetPropertyInfoList(Type type)
        {
            List<PropertyInfo> propertyInfos = new List<PropertyInfo>();

            propertyInfos.AddRange(type.GetProperties(useFlag));
            propertyInfos.AddRange(type.GetProperties(staticFlag));

            return propertyInfos;
        }

        /// <summary>
        /// Returns Property Info for properties of type implementing interface I
        /// </summary>
        /// <typeparam name="I">The base interface to evaluate each property type against</typeparam>
        /// <param name="type">The type to search for properties of interface I</param>
        /// <returns>A list of PropertyInfo for matching properties</returns>
        public static List<PropertyInfo> GetPropertiesOfInterface<I>(Type type)
        {
            var interfaceType = typeof(I);
            return GetPropertyInfoList(type)
                .Where(p =>
                       ImplementsInterface(p.PropertyType, interfaceType) ||
                       ImplementsInterface(
                           GetGenericTypeArgument(type, p.Name) ?? typeof(object),
                           interfaceType)
                )
                .ToList();
        }

        /// <summary>
        /// Return a list of writeable property names for the class type
        /// </summary>
        /// <param name="type">The class type to retrieve properties from</param>
        /// <returns>List of property names</returns>
        public static List<string> GetWriteableProperties(Type type)
        {
            List<string> list = new List<string>();

            List<PropertyInfo> properties = new List<PropertyInfo>(type.GetProperties(useFlag));
            list.AddRange(from p in properties where p.Name != null && p.CanWrite select p.Name);

            properties = new List<PropertyInfo>(type.GetProperties(staticFlag));
            list.AddRange(from p in properties where p.Name != null && p.CanWrite select p.Name);

            return new List<string>(list.Distinct());
        }

        /// <summary>
        /// Returns the Type specified by the name if its loaded in the AppDomain
        /// </summary>
        /// <param name="typeName">The name of the type to get</param>
        /// <returns>The Type object specified by typeName</returns>
        public static Type GetType(string typeName)
        {
            Type type = Type.GetType(typeName);
            if (type == null)
            {
                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    type = assembly.GetType(typeName, false, true);
                    if (type != null)
                        break;
                }
            }
            return type;
        }


        public static bool HasBaseTypeOf(Type type, Type baseType)
        {
            var key = Tuple.Create(type, baseType);
            bool hasBase = false;
            lock(_baseTypeCache)
            {
                if(!_baseTypeCache.TryGetValue(key, out hasBase))
                {
                    if(type.IsSubclassOf(baseType))
                        hasBase = true;
                    else if(ImplementsInterface(type, baseType))
                        hasBase = true;
                    else if (type.BaseType == baseType)
                        hasBase = true;
                    else if (type.IsGenericType)
                    {
                        var genericTypeDefinition = type.GetGenericTypeDefinition();
                        if(genericTypeDefinition.Equals(baseType))
                        {
                            hasBase = true;
                        }
                        else
                        {
                            var typeHasBase = HasBaseTypeOf(type, genericTypeDefinition);
                            var targetArgs = type.GetGenericArguments();
                            var sourceArgs = baseType.GetGenericArguments();

                            if (typeHasBase && targetArgs.Count() == sourceArgs.Count() && targetArgs.SequenceEqual(sourceArgs))
                                hasBase = true;
                        }
                    }
                    else if (type.BaseType != typeof(object) && type.BaseType != null)
                        hasBase = HasBaseTypeOf(type.BaseType, baseType);

                    _baseTypeCache.Add(key, hasBase);
                }
            }
            return hasBase;
        }


        public static bool HasDefaultConstructor(Type type)
        {
            return type.GetConstructors().FirstOrDefault(c => c.GetParameters().Length == 0 && c.IsPublic) != null;
        }

        /// <summary>
        /// Returns a boolean indicating if the field of the provided name exists within this class type
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <param name="fieldName">Case-Sensitive field name</param>
        /// <returns>True if the class type contains a field by the name provided, false if not</returns>
        public static bool HasField(Type type, string fieldName)
        {
            return (GetMemberInfo(type, fieldName, MemberTypes.Field, recursive).Count > 0);
        }

        /// <summary>
        /// Returns a boolean indicating if the method of the provided name exists within this class type
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <param name="methodName">Case-Sensitive method name</param>
        /// <returns>True if the class type contains a method by the name provided, false if not</returns>
        public static bool HasMethod(Type type, string methodName)
        {
            return (GetMemberInfo(type, methodName, MemberTypes.Method, recursive).Count > 0);
        }

        /// <summary>
        /// Returns a boolean indicating if the property of the provided name exists within this class type
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <param name="propertyName">Case-Sensitive property name</param>
        /// <returns>True if the class type contains a property by the name provided, false if not</returns>
        public static bool HasProperty(Type type, string propertyName)
        {
            return (GetMemberInfo(type, propertyName, MemberTypes.Property, recursive).Count > 0);
        }

        /// <summary>
        /// Determines whether type implements interface implementedInterface
        /// </summary>
        /// <param name="type">The type being searched</param>
        /// <param name="implementedInterface">The interface to test for</param>
        /// <returns>A boolean indicating whether or not the type implements the interface</returns>
        public static bool ImplementsInterface(Type type, Type implementedInterface)
        {
            List<Type> types = new List<Type>(type.GetInterfaces());
            if (types.Any(t => t.Equals(implementedInterface) || (t.IsGenericType && t.GetGenericTypeDefinition().Equals(implementedInterface))))
                return true;
            return false;
        }

        /// <summary>
        /// Determines if the child type has the parent type at any point in
        /// its inheritence chain
        /// </summary>
        /// <param name="child"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public static bool InheritsFrom(Type child, Type parent)
        {
            return GetInheritenceChain(child).Contains(parent);
        }

        /// <summary>
        /// Determine if the provided instance implements the ICollection interface
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <returns>True if the class instance implements ICollection, false if it does not</returns>
        public static bool IsCollection(object instance)
        {
            return IsCollection(instance.GetType());
        }

        /// <summary>
        /// Determine if the provided class type implements the ICollection interface
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <returns>True if the class type implements ICollection, false if it does not</returns>
        public static bool IsCollection(Type type)
        {
            return type.GetInterface("ICollection") != null;
        }

        /// <summary>
        /// Determine if the provided instance implements the IDictionary interface
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <returns>True if the class instance implements IDictionary, false if it does not</returns>
        public static bool IsDictionary(object instance)
        {
            return IsDictionary(instance.GetType());
        }

        /// <summary>
        /// Determine if the provided class type implements the IDictionary interface
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <returns>True if the class type implements IDictionary, false if it does not</returns>
        public static bool IsDictionary(Type type)
        {
            return type.GetInterface("IDictionary") != null;
        }

        /// <summary>
        /// Determine if the provided instance implements the IEnumerable interface
        /// </summary>
        /// <param name="instance">The class instance to check</param>
        /// <returns>True if the class instance implements IEnumerable, false if it does not</returns>
        public static bool IsEnumerable(object instance)
        {
            return IsEnumerable(instance.GetType());
        }

        /// <summary>
        /// Determine if the provided class type implements the IEnumerable interface
        /// </summary>
        /// <param name="type">The class type to check</param>
        /// <returns>True if the class type implements IEnumerable, false if it does not</returns>
        public static bool IsEnumerable(Type type)
        {
            return type.GetInterface("IEnumerable") != null;
        }


        public static Type LoadType(string typeName)
        {
            string[] fqtnSegments = typeName.Split(',');
            if (fqtnSegments.Length > 1)
            {
                return LoadTypeByName(fqtnSegments[0].Trim(), fqtnSegments[1].Trim());
            }
            else
            {
                return LoadTypeByName(typeName);
            }
        }

        /// <summary>
        /// Reads the value of either a property or field specified by the member name from the
        /// target class instance.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="memberPath">The case-sensitive member name</param>
        /// <returns>The member's value</returns>
        /// <exception cref="ArgumentException">ArgumentException is thrown when target is null or
        /// memberPath is null or empty.</exception>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist in the target type or
        /// if the member is write only.</exception>
        public static object Read(object target, string memberPath)
        {
            return cache.Read(target, memberPath);
        }

        /// <summary>
        /// Subtracts the given number to the member of the instance
        /// </summary>
        /// <param name="target">The instance to alter</param>
        /// <param name="memberName">The member name to subtract the number from</param>
        /// <param name="value">The amount to subtract</param>
        public static void SubtractFrom(object target, string memberName, Number value)
        {
            Number original = (Number)Read(target, memberName);
            Write(target, memberName, original - value);
        }

        /// <summary>
        /// Casts both types to a single type implementing IComparable and returns the comparison
        /// </summary>
        /// <param name="comparer">The object to compare against</param>
        /// <param name="comparee">The object to compare to</param>
        /// <param name="type">The type to cast to implementing IComparable</param>
        /// <returns>The result of the comparison</returns>
        public static int TypeCastCompare(object comparer, object comparee, Type type)
        {
            if (ImplementsInterface(type, typeof (IComparable)))
            {
                object typedComparer = Convert.ChangeType(comparer, type);
                object typedComparee = Convert.ChangeType(comparee, type);

                return ((IComparable) typedComparer).CompareTo(typedComparee);
            }

            throw new ReflectorException(
                "TypeCastCompare could not compare objects because {0} does not implement IComparable.",
                type.Name);
        }

        /// <summary>
        /// Writes the value to the property or field specified by the member name using auto type conversion.
        /// </summary>
        /// <param name="target">The class instance containing the member to write to</param>
        /// <param name="memberPath">The case-sensitive member name</param>
        /// <param name="value">The value to write</param>
        /// <returns>True if writing was succesful, false if otherwise</returns>
        /// <exception cref="ArgumentException">ArgumentException is thrown when target is null or
        /// memberPath is null or empty.</exception>
        /// <exception cref="ReflectorException">Exception is thrown if the field doesn't exist in the target type,
        ///	if the member is write only, if the target member and value type are incompatible, or if the
        ///	additional arguments passed in are incorrect.</exception>
        public static void Write(object target, string memberPath, object value)
        {
            cache.Write(target, memberPath, value);
        }

        #endregion

        #region Private Methods
        
        /// <summary>
        /// Returns a specific field info object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="fieldName">The case sensitive field name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited fields</param>
        /// <returns>A FieldInfo object matching the constraints provided</returns>
        public static FieldInfo GetFieldInfo(Type type, string fieldName, bool searchHierarchy)
        {
            FieldInfo fieldInfo = type.GetField(fieldName, useFlag);
            if (fieldInfo == null)
            {
                fieldInfo = type.GetField(fieldName, staticFlag);
            }

            return (searchHierarchy && fieldInfo == null & type.BaseType != typeof (object))
                       ?
                           GetFieldInfo(type.BaseType, fieldName, true)
                       :
                           fieldInfo;
        }

        /// <summary>
        /// Returns a specific field info object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="memberName">The case sensitive member name</param>
        /// <param name="types">A list of member types to search within</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited members</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        public static List<MemberInfo> GetMemberInfo(Type type, string memberName, MemberTypes types,
                                                      bool searchHierarchy)
        {
            List<MemberInfo> memberInfo = new List<MemberInfo>();
            memberInfo.AddRange((IEnumerable<MemberInfo>) type.GetMember(memberName, types, useFlag));
            memberInfo.AddRange((IEnumerable<MemberInfo>) type.GetMember(memberName, types, staticFlag));

            return (searchHierarchy && memberInfo.Count <= 0 & type.BaseType != typeof (object))
                       ?
                           GetMemberInfo(type.BaseType, memberName, types, true)
                       :
                           memberInfo;
        }
        
        /// <summary>
        /// Returns a specific MethodInfo object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="methodName">The case sensitive method name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited methods</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        public static List<MethodInfo> GetMethodInfo(Type type, string methodName, bool searchHierarchy)
        {
            MethodInfo methodInfo = type.GetMethod(methodName, useFlag);

            if (searchHierarchy && methodInfo == null & type.BaseType != typeof (object))
            {
                return GetMethodInfo(type.BaseType, methodName, searchHierarchy);
            }
            return new List<MethodInfo>(new[] {methodInfo});
        }

        /// <summary>
        /// Returns a specific MethodInfo object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="methodName">The case sensitive method name</param>
        /// <param name="parameters"></param>
        /// <param name="isStatic"></param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited methods</param>
        /// <returns>A list of MethodInfo objects matching the constraints provided</returns>
        public static List<MethodInfo> GetMethodInfo(Type type, string methodName, object[] parameters, bool isStatic, bool searchHierarchy)
        {
            var callConvention = isStatic ? CallingConventions.Standard : CallingConventions.HasThis;
            var methods = type.GetMethods(isStatic ? staticFlag : useFlag);

            var parameterTypes = GetTypeArray(parameters);
            var methodsMatchingName = methods.Where(m => m.Name == methodName);
            var methodInfo = methodsMatchingName.FirstOrDefault(m =>
                                                                    {
                                                                        var enumerable = ProjectParameterTypes(m);
                                                                        return
                                                                            parameterTypes.SequenceEqual(enumerable,
                                                                                new TypeEquivalenceComparer());
                                                                    });

            if (searchHierarchy && methodInfo == null & type.BaseType != typeof (object))
            {
                return GetMethodInfo(type.BaseType, methodName, parameters, isStatic, searchHierarchy);
            }
            return new List<MethodInfo>(new[] {methodInfo});
        }

        /// <summary>
        /// Returns a specific PropertyInfo object from the type
        /// </summary>
        /// <param name="type">The System.Type to search</param>
        /// <param name="propertyName">The case sensitive property name</param>
        /// <param name="searchHierarchy">A boolean indicating whether or not to search inherited properties</param>
        /// <returns>A PropertyInfo object matching the constraints provided</returns>
        public static PropertyInfo GetPropertyInfo(Type type, string propertyName, bool searchHierarchy)
        {
            PropertyInfo propertyInfo = type.GetProperty(propertyName, useFlag);
            if (propertyInfo == null)
            {
                propertyInfo = type.GetProperty(propertyName, staticFlag);
            }

            return (searchHierarchy && propertyInfo == null & type.BaseType != typeof (object))
                       ?
                           GetPropertyInfo(type.BaseType, propertyName, true)
                       :
                           propertyInfo;
        }
        
        #endregion

        /// <summary>
        /// This internal default constructor was added to prevent attempts to instance Reflector.
        /// </summary>
        internal Reflector()
        {
        }
        public static List<EventInfo> GetEventInfo(object obj)
        {
            Type objType = obj.GetType();
            return objType.GetEvents().ToList();
        }
    }
}